/* 
 *	Copyright (C) 2007 cooleyes
 *	eyes.cooleyes@gmail.com 
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */
 
#include "m4a_parser.h"
#include "common/mem64.h"
#include "mp4info.h"
#include <string.h>

//static uint32_t aac_samplerates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000};

static unsigned int get_sample_size(mp4info_track_t* track, unsigned int sample) {
	return (track->stsz_sample_size ? track->stsz_sample_size : track->stsz_sample_size_table[sample]);
}

static int get_sample_offset(mp4info_track_t* track, unsigned int sample) {
	unsigned int k;
	unsigned int first_sample, last_sample;
	first_sample = last_sample = 0;
	for( k = 0; k < track->stco_entry_count; k++ ) {
		
		unsigned int i, j;
		for( i = 0; i < track->stsc_entry_count-1; i++ ) {
			if ( (k+1) >= track->stsc_first_chunk[i] && (k+1) < track->stsc_first_chunk[i+1] )
				break;
		}
		first_sample = 0;
		for( j = 0; j < i; j++ ) {
			first_sample += ( ( track->stsc_first_chunk[j+1] - track->stsc_first_chunk[j] ) * track->stsc_samples_per_chunk[j] );
		}
		first_sample += ( ( (k+1) - track->stsc_first_chunk[i] ) * track->stsc_samples_per_chunk[i] );
		last_sample = first_sample + track->stsc_samples_per_chunk[i] - 1;
		
		if ( sample >= first_sample && sample <= last_sample )
			break;
	}
	int pos = track->stco_chunk_offset[k];
	while( first_sample < sample ) {
		pos += get_sample_size(track, first_sample);
		first_sample++;
	}
	return pos;
}

M4aParser::M4aParser() {
	initialize();
};

M4aParser::~M4aParser() {
	close();
};

void M4aParser::initialize() {
	is_open = false;
	
	total_frames = 0;
	total_samples = 0;
	current_frame = 0;
	
	frame_index = 0;
	reader = 0;
	output_buffer = 0;
	read_buffer = 0;
	
	codec = 0;
	codec_initialize = false;
	
	memset(&init_data, 0, sizeof(AudioCodecInitData));
};

void M4aParser::finalize() {
	if ( reader ) {
		buffered_reader_close(reader);
	}
	if ( frame_index ) {
		free(frame_index);
	}
	if ( output_buffer ) {
		free(output_buffer);
	}
	if ( read_buffer ) {
		free(read_buffer);
	}
	if ( codec ) {
		if ( codec_initialize )
			codec->finalize();
		delete codec;
	}
	initialize();
};


char* M4aParser::open(const char* filename) {
	
	close();
	
	mp4info_t* info = mp4info_open(filename);
	if( info == 0 ) {
		finalize();
		return "M4aParser : get MP4 info fail";
	}
	int i;
	unsigned int j;	
	for(i = 0; i < info->total_tracks; i++) {
		mp4info_track_t* track = info->tracks[i];
		if (track->type != TRACK_AUDIO)
			continue;
		if ( track->audio_type != 0x6D703461 /*mp4a*/)
			continue;
		
		init_data.samplerate = track->samplerate;
		init_data.channels = 2;		
		init_data.sample_bits = 0x10;
		init_data.samples_per_frame = 1024;
		init_data.samples_per_decoded = init_data.samples_per_frame;
		
		total_frames = 0;
		for( j = 0; j < track->stts_entry_count; j++)
			total_frames += track->stts_sample_count[j];
		total_samples = total_frames * init_data.samples_per_frame;
		frame_index = (AudioFrameIndex*)malloc_64(total_frames * sizeof(AudioFrameIndex));
		if ( frame_index == 0 ) {
			mp4info_close(info);
			finalize();
			return "M4aParser : malloc seek_table fail";
		}
		init_data.frame_align = 0;
		for( j = 0; j<total_frames; j++) {
			frame_index[j].frame_position = get_sample_offset(track, j);
			frame_index[j].frame_size = get_sample_size(track, j);
			if ( frame_index[j].frame_size > init_data.frame_align )
				init_data.frame_align = frame_index[j].frame_size;
		}
		aac_data_start = get_sample_offset(track, 0);
		break;
	}
	if ( i == info->total_tracks ) {
		mp4info_close(info);
		finalize();
		return "M4aParser : audio_tracks = 0";
	}
	
	mp4info_close(info);
	
	reader = buffered_reader_open(filename, 65536, 1);
	if ( !reader ) {
		finalize();
		return "M4aParser : open buffered_reader fail";
	}
		
	buffered_reader_seek(reader, aac_data_start);
	
	output_buffer = malloc_64(init_data.samples_per_frame<<2);
	if ( output_buffer == 0 ) {
		finalize();
		return "M4aParser : malloc output_buffer fail";
	}
	
	read_buffer = malloc_64(init_data.frame_align);
	if ( read_buffer == 0 ) {
		finalize();
		return "M4aParser : malloc read_buffer fail";
	}
	
	codec = AudioCodec::create_audio_codec(AudioCodec::AAC);
	if ( !codec ) {
		finalize();
		return "M4aParser : create audio codec fail";
	}
	char* result = codec->initialize(&init_data);
	if ( result ) {
		finalize();
		return result;
	}
	codec_initialize = true;
	
	is_open = true;
	return 0;
};

void M4aParser::close() {
	if ( is_open ) {
		finalize();
	}
};
	
uint32_t M4aParser::get_samplerate() {
	return init_data.samplerate;
};

uint32_t M4aParser::get_channels() {
	return init_data.channels;
};

uint64_t M4aParser::get_duration() {
	uint64_t duration = 1000000LL*total_samples/init_data.samplerate;
	return duration;
};
	
uint32_t M4aParser::get_samples_per_frame() {
	return init_data.samples_per_frame;
};

uint32_t M4aParser::get_max_samples_per_frame() {
	return init_data.samples_per_frame;
};

uint64_t M4aParser::seek_time(uint64_t time) {
	uint32_t samples = (uint32_t)(time*init_data.samplerate/1000000LL);
	if ( samples > total_samples )
		samples = total_samples;
	
	current_frame = samples/init_data.samples_per_frame;
	
	if (current_frame >= total_frames)
		current_frame = total_frames - 1;
	
	buffered_reader_seek(reader, frame_index[current_frame].frame_position);
	
	uint64_t current = 1000000LL*current_frame*init_data.samples_per_frame/init_data.samplerate;
	return current;
};

char* M4aParser::get_frame(void** pcm, uint32_t* samples) {
	
	if ( !is_open )
		return "M4aParser : parser is not open";
	
	if ( current_frame >= total_frames ) {
		*pcm = output_buffer;
		*samples = 0;
	}
	else {
		memset(read_buffer, 0, init_data.frame_align);
		uint32_t read_size;
		read_size = buffered_reader_read(reader, read_buffer, frame_index[current_frame].frame_size);
		if ( read_size != (frame_index[current_frame].frame_size) ) {
			*pcm = output_buffer;
			*samples = 0;
		}
		else {
			current_frame++;
			memset(output_buffer, 0, init_data.samples_per_frame << 2);
			int32_t get_samples = codec->decode(read_buffer, read_size, output_buffer);
			*samples = (get_samples<0) ? 0 : get_samples;
			*pcm = output_buffer;
		}
	}
	return 0;
};



